#第七章 高级特性
#https://liaoxuefeng.com/books/python/advanced/slice/index.html
#===============================================================================================
#7.1 切片
#取一个list或tuple的部分元素是非常常见的操作
l=[i for i in range(100)]
# list名[起始位置：结束位置：步长]
print(l[0:10],l[:10],l[:10:2],sep='\n')#默认起始0，结束为最后一个（可以取到，闭区间；如果手动设置为最后一个-1则不能），步长为1
print(l[-11:-1])#左开右闭区间
print(l[-10:])#默认结束会取到最后一个
print(l[90:-1])#正逆序号可以混用
# 练习题 利用切片操作，实现一个trim()函数，去除字符串首尾的空格，注意不要调用str的strip()方法：
def trim(s):
    l=list(s)
    #去除前面的空格
    #  while  l[0]==' 'and len(l)>0 :l.pop(0)#这个是错误的
    #  while len(l)>0 :l.pop(0)#这个同理
    while len(l)>0 and l[0]==' ':l.pop(0)#不判断长度在删掉最后一个元素时，再l[0]会报错，而且必须把长度判断放前面， 否则会报错
    #出去后面的空格
    while len(l)>0 and  l[-1]==' ':l.pop(-1)
    s=''.join(l)
    return s
print(trim('  hello world   '),len(trim('  hello world   ')),sep='~')
if trim('hello  ') != 'hello':
    print('测试失败!')
elif trim('  hello') != 'hello':
    print('测试失败!')
elif trim('  hello  ') != 'hello':
    print('测试失败!')
elif trim('  hello  world  ') != 'hello  world':
    print('测试失败!')
elif trim('') != '':
    print('测试失败!')
elif trim('    ') != '':
    print('测试失败!')
else:
    print('测试成功!')
#===============================================================================================
#7.2 迭代
#用于list 和 tuple
l=[i for i in range(5)]
for i in l:print(i,end='~')
#迭代字符串
for i in 'hello world':print(i,end='~')
print()
#迭代dict
d={'a':1,'b':2,'c':3}
for  k in d:print(i,end='~')#默认迭代key
print()
for v in d.values():print(v,end='~')#迭代value
print()
for k,v in d.items():print(k,v,sep=',',end='~')#迭代key-value
print()
#判断是否可迭代
from collections.abc import Iterable
print(isinstance('abc',Iterable))#str是否可迭代
print(isinstance([1,2,3],Iterable))#list是否可迭代
print(isinstance(123,Iterable))#int是否可迭代
#练习题
#请使用迭代查找一个list中最小和最大值，并返回一个tuple：
def findMinAndMax(l):
    if len(l)==0:return (None,None)
    max=min=l[0]
    for i in l:
        if i>max:max=i
        elif i<min:min=i
    return (min,max)
# 测试
if findMinAndMax([]) != (None, None):
    print('1测试失败!')
elif findMinAndMax([7]) != (7, 7):
    print('2测试失败!')
elif findMinAndMax([7, 1]) != (1, 7):
    print('3测试失败!')
elif findMinAndMax([7, 1, 3, 9, 5]) != (1, 9):
    print('4测试失败!')
else:
    print('5测试成功!')
#===============================================================================================
#7.3 列表生成式
#类似于for循环生成列表
l1=list(range(5))
l2=[x**2 for x in range(5)]
l3=[x**2 for x in range(5) if x%2==0]#加入if判断
l4=[m+n for m in 'abc' for n in 'xyz']#两层循环
print(l1,l2,l3,l4,sep='\n')
d = {'x': 'A', 'y': 'B', 'z': 'C' }
l5=[k+'='+v for k,v in d.items()]
print(l5)
# 最后把一个list中所有的字符串变成小写：
L= ['Hello', 'World', 'IBM', 'Apple']
l=[s.lower() for s in L]#大写是upper()
print(l)
#if else
# 在一个列表生成式中，for前面的if ... else是表达式，而for后面的if是过滤条件，不能带else。
# for前面的表达式x if x % 2 == 0 else -x才能根据x计算出确定的结果。
l1=[x if x%2==0 else -x for x in range(5)]
l2=[x for x in range(5) if x%2==0]
print('l1=',l1,'l2=',l2,sep='\n')
#判断一个元素是否是字符串
#isinstance('abc', str) # True
#练习题
#将列表中的所有字符串变成小写,其余元素保留
L1 = ['Hello', 'World', 18, 'Apple', None]
L2=[s.lower()  if isinstance(s,str) else s for s in L1]
print(L1,L2)
#===============================================================================================
#7.4 生成器
# 通过列表生成式，我们可以直接创建一个列表。但是，受到内存限制，列表容量肯定是有限的。
# 如果列表元素可以按照某种算法推算出来，那我们是否可以在循环的过程中不断推算出后续的元素呢？这样就不必创建完整的list，从而节省大量的空间。在Python中，这种一边循环一边计算的机制，称为生成器：generator
# 创建生成器的方法
# 1.把一个列表生成式的[]改成()，就创建了一个generator
l=[x**2 for x in range(5)]
g=(x**2 for x in range(5))
print(l,g)#l是列表，g是生成器
#获取生成器的元素
print(next(g),next(g),next(g),next(g),next(g))#通过next()函数获得generator的下一个返回值
# 每执行一次next()，就计算出下一个元素的值，直到计算到最后一个元素，没有更多的元素时，抛出StopIteration的错误
# print(next(g))#超出范围会报错,StopIteration
#一般不会用next()获取生成器的元素，而是通过for循环
g=(x**2 for x in range(5))#重新生成生成器;生成器只能遍历一次,前面next()操作后已经遍历完了
for i in g:print(i,end='~')
print()
# 2.函数生成器
# 如果一个函数定义中包含yield关键字，那么这个函数就不再是一个普通函数，而是一个generator
#每次遇到yeild函数就会返回，下次调用时从上次返回的地方（上一个yeild的下一句）继续执行
#yield 关键字
def func1():
    print('step1')
    yield 1
    print('step2')
    yield 2
    print('step3')
    yield 3
    return 'done'
a=func1()
print(next(a))
print(next(a))
print(next(func1()))
print(next(func1()))#每次调用都会生成一个新的生成器
#斐波拉契数列
def fib(n):
    i,a,b=0,0,1
    while i<n:
        a,b=b,a+b
        i+=1
        yield a
    return 'done'
g=fib(5)
for i in g:print(i,end='~')
#for循环调用不能获取返回值，要获取返回值需要捕获StopIteration错误
g=fib(5)
while True:
    try:
        x=next(g)
        print(x,end='~')
    except StopIteration as e:
        print(e.value)
        break
#练习题
#杨辉三角，这个生成器每执行一次会生成一行
#理想的效果下应该写成输入最多多少行，死循环不好
def triangle():
    l=[1]
    while True:
        yield l
        temp=[]
        if(len(l)>1):
            for i in range(len(l)-1):
                temp.append(l[i]+l[i+1])#排除第一个和最后一个元素
        temp.insert(0,1)#首部插入1
        temp.append(1)#尾部插入1
        l=temp
a=triangle()
print(next(a),next(a),next(a),next(a),next(a))
n=0
for i in triangle():
    n+=1
    if n==10:break#必须强制推出，因为这个生成器内部是死循环
    print(i)
#和用while + next()是一样的
#测试
n = 0
results = []
for t in triangle():
    results.append(t)
    n = n + 1
    if n == 10:
        break
for t in results:
    print(t)

if results == [
    [1],
    [1, 1],
    [1, 2, 1],
    [1, 3, 3, 1],
    [1, 4, 6, 4, 1],
    [1, 5, 10, 10, 5, 1],
    [1, 6, 15, 20, 15, 6, 1],
    [1, 7, 21, 35, 35, 21, 7, 1],
    [1, 8, 28, 56, 70, 56, 28, 8, 1],
    [1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
]:
    print('测试通过!')
else:
    print('测试失败!')
#===============================================================================================
#7.5 迭代器
# 凡是可作用于for循环的对象都是Iterable类型；
# 凡是可作用于next()函数的对象都是Iterator类型，它们表示一个惰性计算的序列，Iterator的计算是惰性的，只有在需要返回下一个数据时它才会计算。
# 集合数据类型如list、dict、str等是Iterable但不是Iterator，不过可以通过iter()函数获得一个Iterator对象。
# 生成器都是Itoerable类型，也是Iterator
# 可以使用isinstance()判断一个对象是否是Iterator对象
from collections.abc import Iterator
print(isinstance([1,2],Iterable))#list是Iterable的
print(isinstance([1,2],Iterator))#list不是Iterator
print(isinstance((x**2 for x in range(5)),Iterable))#生成器是Iterable
print(isinstance((x**2 for x in range(5)),Iterator))#生成器是Iterator
#把list、dict、str等Iterable变成Iterator可以使用iter()函数
print(isinstance(iter([1,2]),Iterator))#list变成Iterator
#Iterator对象表示的是一个数据流，可以把这个数据流看做是一个有序序列，但我们却不能提前知道序列的长度，只能不断通过next()函数实现按需计算下一个数据。
#Iterator甚至可以表示一个无限大的数据流，例如全体自然数。而使用list是永远不可能存储全体自然数的。